﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Reporting.WebForms;

namespace Lukianol.Web.Report
{
    public sealed class SqlReportDataProvider
    {
        public SqlReportDataProvider(Uri reportServerUrl, string reportName)
            : this(reportServerUrl, reportName, SqlReportFormatInfoFactory.Html4)
        {   
           
        }

        public SqlReportDataProvider(Uri reportServerUrl, string reportName, string format)
            : this(reportServerUrl, reportName, SqlReportFormatInfoFactory.Current.GetInstance(format))
        {
            
        }

        private SqlReportDataProvider(Uri reportServerUrl, string reportName, IReportFormatInfoProvider reportFormatInfoProvider)
        {
            if (reportServerUrl == null)
                throw new ArgumentNullException("reportServerUrl");
            if (reportName == null)
                throw new ArgumentNullException("reportName");
            if (reportFormatInfoProvider == null) 
                throw new ArgumentNullException("reportFormatInfoProvider");

            ReportServerUrl = reportServerUrl;
            ReportName = reportName;
            ReportFormatInfoProvider = reportFormatInfoProvider;
        }

        public Uri ReportServerUrl { get; private set; }
        
        public string ReportName { get; private set; }
        
        public IReportFormatInfoProvider ReportFormatInfoProvider { get; set; }

        public ReportDataParameter[] GetParameters()
        {
            return ServerReport.GetParameters()
                .Where(p => p.Visible)
                .Select(p => new ReportDataParameter(p.Name, p.Prompt, Convert.ToString(p.DataType), p.Values.FirstOrDefault()))
                .ToArray();
        }
        
        public IEnumerable<KeyValuePair<string, string>> GetFormats()
        {
            return ServerReport.ListRenderingExtensions()
                .Where(e => e.Visible)
                .Select(e => new KeyValuePair<string, string>(e.Name, e.LocalizedName));
        }

        public void SetParameters(IEnumerable<ReportDataParameter> parameters)
        {
            if (parameters == null)
                throw new ArgumentNullException("parameters");

            var projected = parameters.Select(p => new ReportParameter(p.Name, p.Value));
            ServerReport.SetParameters(projected);
        }

        public byte[] GetData()
        {
            var serverReport = ServerReport;
            var data = serverReport.Render(ReportFormatInfoProvider.Name, GetDeviceInfo(ReportFormatInfoProvider.GetParameters()));
            _totalPageNumber = serverReport.GetTotalPages();
            return data;
        }

        private int? _totalPageNumber;
        public int TotalPageNumber
        {
            get 
            {
                if (_totalPageNumber.HasValue)
                    return _totalPageNumber.Value;

                throw new InvalidOperationException(Exceptions.CallGetDataAtFirst);
            }
        }

        private ServerReport _serverReport;
        
        private ServerReport ServerReport
        {
            get
            {
                return _serverReport ?? (_serverReport =  new ServerReport
                                                            {
                                                                ReportPath = ReportName,
                                                                ReportServerUrl = ReportServerUrl
                                                            } );
            }
            
        }

        private static string GetDeviceInfo(IEnumerable<KeyValuePair<string, string>> parameters)
        {

            var root = new XElement("DeviceInfo");

            if (parameters != null && parameters.Any())
                foreach (var parameter in parameters)
                {
                    root.Add(new XElement(parameter.Key, parameter.Value));
                }

            return root.ToString();
        }
    }
}